
namespace JshRCSWebApi.API
{
    public class Program
    {
        static string CustomCorsPolicy = "CustomCorsPolicy";

        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            #region 配置文件载入
            SysConfig.InitConfig();
            #endregion

            #region 单例注入
            builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
            builder.Host.ConfigureContainer<ContainerBuilder>(builder =>
            {
                builder.RegisterModule<AutofacModule>();
            });
            #endregion

            #region Json时间格式化
            builder.Services.AddControllers()
            .AddJsonOptions(options =>
            {
                //格式化日期时间格式
                options.JsonSerializerOptions.Converters.Add(new DatetimeJsonConverter("yyyy-MM-dd HH:mm:ss"));
                //数据格式首字母小写
                options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
                //数据格式原样输出
                options.JsonSerializerOptions.PropertyNamingPolicy = null;
                //取消Unicode编码
                options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);
                //忽略空值
                options.JsonSerializerOptions.IgnoreNullValues = true;
                //允许额外符号
                options.JsonSerializerOptions.AllowTrailingCommas = true;
                //反序列化过程中属性名称是否使用不区分大小写的比较
                options.JsonSerializerOptions.PropertyNameCaseInsensitive = false;
            });
            #endregion

            #region SqlSugar注册
            builder.Services.AddSqlsugarSetup();
            builder.Services.AddConfigureServices();
            #endregion

            #region 设置跨域白名单
            //允许一个或多个来源可以跨域
            string Urls = SysConfig.Params.Cors.Urls;//获取配置中的跨域值
            builder.Services.AddCors(options =>
            {
                options.AddPolicy(CustomCorsPolicy, policy =>
                {
                    //允许一个或多个来源可以跨域
                    //string Urls = SysConfig.Params.Cors.Urls;//获取配置中的跨域值
                    //policy
                    //.WithOrigins(Urls.Split(','))//设定允许跨域的来源，有多个可以用','隔开
                    //.AllowAnyHeader()
                    //.AllowAnyMethod()
                    //.AllowCredentials();

                    policy
                    .AllowAnyMethod()
                    .AllowAnyOrigin() //允许任何来源的主机访问   
                    .AllowAnyHeader();
                });
            });
            #endregion

            #region JWT配置
            string Secret = SysConfig.Params.Project.JwtConfig.Secret;
            string Iss = SysConfig.Params.Project.JwtConfig.Iss;
            string Aud = SysConfig.Params.Project.JwtConfig.Aud;
            //生成密钥
            var SigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Secret));
            //认证参数
            //注册服务
            builder.Services.AddAuthentication(options =>
            {
                options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(options =>
            {
                options.TokenValidationParameters = new TokenValidationParameters()
                {
                    ValidateIssuer = true, //是否验证Issuer
                    ValidIssuer = Iss, //发行人Issuer
                    ValidateAudience = true, //是否验证Audience
                    ValidAudience = Aud, //订阅人Audience
                    ValidateIssuerSigningKey = true, //是否验证SecurityKey
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Secret)), //SecurityKey
                    ValidateLifetime = true, //是否验证失效时间
                    ClockSkew = TimeSpan.FromSeconds(30), //过期时间容错值，解决服务器端时间不同步问题（秒）
                    RequireExpirationTime = true,
                };
                options.Events = new JwtBearerEvents//验证失败的自定义返回
                {
                    // 在安全令牌通过验证和ClaimsIdentity通过验证之后调用
                    // 如果用户访问注销页面
                    //OnTokenValidated = context =>
                    //{
                    //    if (context.Request.Path.Value.ToString() == "/account/logout")
                    //    {
                    //        var token = ((context as TokenValidatedContext).SecurityToken as JwtSecurityToken).RawData;
                    //    }
                    //    return Task.CompletedTask;
                    //},
                    //此处为权限验证失败后触发的事件
                    OnChallenge = context =>
                    {
                        //此处代码为终止.Net Core默认的返回类型和数据结果，这个很重要哦，必须
                        context.HandleResponse();
                        //自定义自己想要返回的数据结果，我这里要返回的是Json对象，通过引用Newtonsoft.Json库进行转换
                        var payload = JsonConvert.SerializeObject(new { code = "401", msg = "很抱歉，您无权访问该接口" });
                        //自定义返回的数据类型
                        context.Response.ContentType = "application/json";
                        //自定义返回状态码，默认为401 我这里改成 200
                        context.Response.StatusCode = StatusCodes.Status200OK;
                        //context.Response.StatusCode = StatusCodes.Status401Unauthorized;
                        //输出Json数据结果
                        context.Response.WriteAsync(payload);
                        return Task.FromResult(0);
                    }
                };
            });

            builder.Services.AddHttpContextAccessor();
            builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            builder.Services.AddScoped<AuthenticationFilter>();
            #endregion

            #region 配置SwaggerGen
            // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
            builder.Services.AddEndpointsApiExplorer();
            builder.Services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo()
                {
                    Version = "v1.0.1",
                    Title = "Jsh精实实体RCS接口",
                    Description = $"接口文档说明",
                    Contact = new OpenApiContact()
                    {
                        Name = "",
                        Email = "",
                        Url = null
                    }
                });
                c.CustomOperationIds(apiDesc =>
                {
                    var controllerAction = apiDesc.ActionDescriptor as ControllerActionDescriptor;
                    return controllerAction?.ControllerName + "-" + controllerAction?.ActionName;
                });

                //配置Swagger接口文档注释
                c.IncludeXmlComments("JshRCSWebApi.API.xml", true);

                //定义JwtBearer认证方式一
                c.AddSecurityDefinition("Authorization", new OpenApiSecurityScheme()
                {
                    Description = "直接在输入框中输入认证信息",
                    Name = "Authorization",//jwt默认的参数名称
                    In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
                    Type = SecuritySchemeType.ApiKey,
                    BearerFormat = "JWT",
                    Scheme = "Bearer"
                });
                //声明一个Scheme，注意下面的Id要和上面AddSecurityDefinition中的参数name一致
                var scheme = new OpenApiSecurityScheme()
                {
                    Reference = new OpenApiReference() { Type = ReferenceType.SecurityScheme, Id = "Bearer" }
                };
                //注册全局认证（所有的接口都可以使用认证）
                c.AddSecurityRequirement(new OpenApiSecurityRequirement()
                {
                    [scheme] = new string[0]
                });

                //解决Knife4UI，Authorization自动不会添加
                c.OperationFilter<SwaggerFilter>();

            });
            #endregion

            #region 认证过滤器
            builder.Services.AddMvc(option =>
            {
                option.Filters.Add<AuthenticationFilter>();
            });
            #endregion

            var app = builder.Build();

            //使用全局异常中间件
            app.UseMiddleware<GlobalExceptionMiddleware>();
            // Configure the HTTP request pipeline.
            app.UseHttpsRedirection();
            app.UseRouting();

            #region 认证与授权
            //调用中间件：UseAuthentication（认证），必须在所有需要身份认证的中间件前调用，比如 UseAuthorization（授权）。
            app.UseAuthentication();
            app.UseAuthorization();

            //JWT配置RequireAuthorization，访问所有API需要 jwt验证通过才可以完成
            app.MapControllers().RequireAuthorization();
            #endregion

            #region 设置跨域白名单
            //CORS 中间件必须配置为在对 UseRouting 和 UseEndpoints的调用之间执行。 配置不正确将导致中间件停止正常运行。
            app.UseCors(CustomCorsPolicy);//设置只允许特定来源可以跨域
            #endregion

            #region 配置Swagger
            app.UseSwagger();
            //app.UseSwaggerUI(options =>
            //{
            //    options.SwaggerEndpoint("/swagger/v1/swagger.json", "v1");
            //    //options.HeadContent += $"<script type='text/javascript'>alert('欢迎来到SwaggerUI页面')</script>";
            //});
            app.UseKnife4UI(c =>
            {
                c.RoutePrefix = ""; // serve the UI at root
                c.SwaggerEndpoint("/v1/api-docs", "V1 Docs");
            });

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
                endpoints.MapSwagger("{documentName}/api-docs");
            });
            #endregion

            #region 配置访问地址
            string filepath = AppDomain.CurrentDomain.BaseDirectory + "/File";
            if (!Directory.Exists(filepath))//若文件夹不存在则新建文件夹   
            {
                Directory.CreateDirectory(filepath);//新建文件夹
            }
            app.UseStaticFiles(new StaticFileOptions()//自定义自己的文件路径
            {
                FileProvider = new PhysicalFileProvider(filepath),//路径
                RequestPath = new PathString("/File"),//对外的访问路径
            });
            #endregion 

            app.Run();
        }
    }
}